<template>
  <div>


    <!-- 搜索栏 -->
    <div style="display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:12px">
      <div style="display:flex; align-items:center; gap:8px">
        <el-input
          v-model="searchQuery"
          placeholder="请输入【角色名/角色编码】搜索"
          clearable
          @clear="clearSearch"
          style="width:360px"
        />
      </div>

      <div>
        <!-- hide the add button entirely when user has no add permission -->
        <el-button v-if="canAdd" type="primary" @click="openAdd">新增角色</el-button>
      </div>
    </div>

    <el-table :data="rolesView" stripe border style="width:100%" remote @sort-change="onSortChange" @header-click="onHeaderClick">
      <el-table-column label="序号" width="80">
        <template #default="{ $index }">{{ (page - 1) * pageSize + $index + 1 }}</template>
      </el-table-column>
      <el-table-column prop="role_name" label="角色名" sortable />
      <el-table-column prop="role_code" label="角色编码" sortable />
      <el-table-column prop="role_level" label="角色层级" sortable />
      <el-table-column prop="role_desc" label="描述" />

      <el-table-column label="操作" width="210px">
        <template #default="{ row }">
          <!-- hide buttons when user lacks the corresponding permission -->
          <el-button v-if="canEdit" size="small" @click="openEdit(row)">编辑</el-button>
          <el-button v-if="canDelete" size="small" type="danger" @click="remove(row)" style="margin-left:8px">删除</el-button>
          <el-button v-if="canConfig" size="small" type="success" @click="openPermission(row)" style="margin-left:8px">配置权限</el-button>
        </template>
      </el-table-column>
    </el-table>

    <div style="margin-top:12px; display:flex; justify-content:flex-end; align-items:center">
      <el-pagination
        background
        layout="total, sizes, prev, pager, next"
        :current-page="page"
        :page-size="pageSize"
        :page-sizes="[5,10,20]"
        :total="total"
        @current-change="gotoPage"
        @size-change="onSizeChange"
      />
    </div>

    <el-alert v-if="error" type="error" :title="error" show-icon style="margin-top:12px" />

    <!-- Dialog form -->
    <el-dialog :title="editing.id ? '编辑角色' : '新增角色'" v-model="showModal" width="520px">
      <el-form ref="formRef" :model="editing" :rules="rules" label-width="100px">
        <el-form-item prop="role_name">
          <template #label>
             角色名
          </template>
          <el-input v-model="editing.role_name" />
        </el-form-item>

        <el-form-item label="角色编码">
          <el-input v-model="editing.role_code" />
        </el-form-item>

        <el-form-item label="角色层级">
          <el-input-number v-model.number="editing.role_level" :min="0" />
        </el-form-item>

        <el-form-item label="描述">
          <el-input type="textarea" v-model="editing.role_desc" :rows="4" />
        </el-form-item>

        <el-form-item>
          <div style="text-align:left; color:chocolate">
            提示：角色名不可重复。
          </div>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="closeModal">取消</el-button>
        <!-- only show save when user has proper add/edit permission for the current mode -->
        <el-button v-if="(editing.id && canEdit) || (!editing.id && canAdd)" type="primary" @click="submit">保存</el-button>
      </template>

    </el-dialog>

    <!-- 权限配置对话框 -->
    <el-dialog title="权限配置" v-model="permDialog" width="640px">
      <div v-if="permError" style="margin-bottom:8px"><el-alert type="error" :title="permError" show-icon /></div>
      <div v-if="permLoading" style="text-align:center;padding:16px">加载中...</div>
      <div v-else style="max-height:420px; overflow:auto">
        <el-tree
          ref="permTreeRef"
          :data="menusForPerm"
          node-key="id"
          show-checkbox
          :props="{ children: 'children', label: 'name' }"
          :default-checked-keys="checkedMenuIds"
        />
      </div>
      <template #footer>
        <el-button @click="closePermDialog">取消</el-button>
        <!-- hide save when user cannot configure permissions -->
        <el-button v-if="canConfig" type="primary" @click="savePerm">保存</el-button>
      </template>
    </el-dialog>

  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, reactive, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useAuthStore } from '../stores/auth'

const auth = useAuthStore()
// permission flags for this page
const canAdd = computed(() => auth.hasPermission('0102', '新增'))
const canEdit = computed(() => auth.hasPermission('0102', '编辑'))
const canDelete = computed(() => auth.hasPermission('0102', '删除'))
const canConfig = computed(() => auth.hasPermission('0102', '配置权限'))

const allRoles = ref<any[]>([])
const roles = ref<any[]>([])
const error = ref('')

// new: permission dialog state
const permDialog = ref(false)
const permRoleId = ref<number | null>(null)
const menusForPerm = ref<any[]>([])
const checkedMenuIds = ref<number[]>([])
const permLoading = ref(false)
const permError = ref('')
const permTreeRef = ref<any>(null)

// search
const searchQuery = ref('')
const searchTimer = ref<number | null>(null)

// pagination
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))

// sorting (server-side when available)
const sortBy = ref<string | null>(null)
const sortOrder = ref<'asc' | 'desc' | null>(null)

const showModal = ref(false)
const editing = reactive<any>({ id: null, role_name: '', role_desc: '', role_code: '', role_level: null, create_time: null, update_time: null })
const formRef = ref<any>(null)

const rules = {
  role_name: [{ required: true, message: '请输入角色名', trigger: ['blur', 'change'] }]
}

const rolesView = computed(() => roles.value)

async function load() {
  error.value = ''
  try {
    // If sorting or searching is requested, fetch full dataset and operate client-side
    if (sortBy.value || (searchQuery.value && String(searchQuery.value).trim() !== '')) {
      const res = await auth.apiFetch('/roles')
      const all = (res.roles || res || []).map((r: any) => ({ ...normalizeRole(r) }))

      const q = String(searchQuery.value || '').trim().toLowerCase()
      let filtered = all
      if (q) {
        filtered = all.filter((r: any) => {
          const rn = String(r.role_name || '').toLowerCase()
          const rc = String(r.role_code || '').toLowerCase()
          const rd = String(r.role_desc || '').toLowerCase()
          return rn.includes(q) || rc.includes(q) || rd.includes(q)
        })
      }

      const prop = sortBy.value
      const order = sortOrder.value === 'asc' ? 1 : -1
      if (prop) {
        filtered.sort((a: any, b: any) => {
          const va = a[prop] ?? ''
          const vb = b[prop] ?? ''
          if (typeof va === 'number' && typeof vb === 'number') return (va - vb) * order
          const sa = String(va).toLowerCase()
          const sb = String(vb).toLowerCase()
          if (sa < sb) return -1 * order
          if (sa > sb) return 1 * order
          return 0
        })
      }

      total.value = filtered.length
      roles.value = filtered.slice((page.value - 1) * pageSize.value, page.value * pageSize.value)
    } else {
      // try to use paginated endpoint if backend supports it (many backends don't for roles)
      try {
        const params = new URLSearchParams()
        params.set('page', String(page.value))
        params.set('limit', String(pageSize.value))
        if (sortBy.value) params.set('sort_by', sortBy.value)
        if (sortOrder.value) params.set('sort_order', sortOrder.value)
        const res = await auth.apiFetch(`/roles?${params.toString()}`)
        // normalize
        const fetched = (res.roles || res || []) .map((r: any) => ({ ...normalizeRole(r) }))
        roles.value = fetched
        total.value = typeof res.total === 'number' ? res.total : fetched.length
      } catch (e) {
        // fallback to fetching all and paginate locally
        const res = await auth.apiFetch('/roles')
        const all = (res.roles || res || []).map((r: any) => ({ ...normalizeRole(r) }))
        total.value = all.length
        roles.value = all.slice((page.value - 1) * pageSize.value, page.value * pageSize.value)
      }
    }
  } catch (e) {
    
  }
}

function normalizeRole(r: any) {
  return {
    id: r.id,
    role_name: r.role_name || r.name || r.roleName || '',
    role_desc: r.role_desc || r.description || r.desc || '',
    role_code: r.role_code || r.code || '',
    role_level: typeof r.role_level !== 'undefined' ? r.role_level : (r.level ?? null),
    create_time: r.create_time || r.created || r.create_time || null,
    update_time: r.update_time || r.updated || null
  }
}

onMounted(() => {
  load()
})

async function openAdd() {
  if (!canAdd.value) { ElMessage.warning('没有新增权限'); return }
  editing.id = null
  editing.role_name = ''
  editing.role_code = ''
  editing.role_level = null
  editing.role_desc = ''
  showModal.value = true
}

async function openEdit(r: any) {
  if (!canEdit.value) { ElMessage.warning('没有编辑权限'); return }
  editing.id = r.id
  editing.role_name = r.role_name
  editing.role_code = r.role_code
  editing.role_level = r.role_level
  editing.role_desc = r.role_desc
  showModal.value = true
}

// new: open permission dialog
async function openPermission(r: any) {
  if (!canConfig.value) { ElMessage.warning('没有配置权限'); return }
  permError.value = ''
  permLoading.value = true
  permRoleId.value = r.id
  try {
    const mres = await auth.apiFetch('/menus')
    menusForPerm.value = mres.menus || mres || []
  } catch (e) {
    menusForPerm.value = []
    permError.value = '加载菜单失败'
  }
  try {
    const pres = await auth.apiFetch(`/roles/${r.id}/menus`)
    const rawIds: number[] = Array.isArray(pres.menu_ids) ? pres.menu_ids.map((x: any) => Number(x)).filter(n => !Number.isNaN(n)) : []

    // Build id -> node map for quick lookup
    const idMap = new Map<number, any>()
    function buildMap(nodes: any[]) {
      for (const n of nodes || []) {
        idMap.set(n.id, n)
        if (n.children && n.children.length) buildMap(n.children)
      }
    }
    buildMap(menusForPerm.value)

    const rawSet = new Set(rawIds)

    // helper to collect all descendant ids of a node
    function collectDescendants(node: any, out: number[]) {
      if (!node || !node.children || node.children.length === 0) return
      for (const c of node.children) {
        out.push(c.id)
        collectDescendants(c, out)
      }
    }

    // compute displayed checked ids: include a node id when
    // - it's a leaf and present in rawSet, OR
    // - all of its descendant ids are present in rawSet (so parent should be shown checked)
    const displayed: number[] = []
    for (const id of rawSet) {
      const node = idMap.get(id)
      if (!node) continue
      if (!node.children || node.children.length === 0) {
        displayed.push(id)
        continue
      }
      const desc: number[] = []
      collectDescendants(node, desc)
      // if node has descendants, consider it checked only when all descendants are present in rawSet
      const allDescPresent = desc.length === 0 ? true : desc.every(did => rawSet.has(did))
      if (allDescPresent) displayed.push(id)
    }

    // set the checked keys used by the tree UI
    checkedMenuIds.value = displayed
  } catch (e) {
    checkedMenuIds.value = []
    permError.value = permError.value ? permError.value : '加载已分配权限失败'
  }
  permLoading.value = false
  permDialog.value = true
}

function closePermDialog() {
  permDialog.value = false
  permRoleId.value = null
  checkedMenuIds.value = []
}

async function savePerm() {
  if (!permRoleId.value) return
  if (!canConfig.value) { ElMessage.error('没有配置权限'); return }
  permError.value = ''
  try {
    // get checked keys from tree if available to respect current UI state
    let keys: number[] = checkedMenuIds.value || []
    if (permTreeRef.value && typeof permTreeRef.value.getCheckedKeys === 'function') {
      try {
        const k = permTreeRef.value.getCheckedKeys() as any
        // ensure numeric ids
        keys = (k || []).map((x: any) => Number(x)).filter((n: any) => !Number.isNaN(n))
      } catch (err) {
        // fallback to previously loaded keys
      }
    }
    await auth.apiFetch(`/roles/${permRoleId.value}/menus`, { method: 'PUT', body: JSON.stringify({ menu_ids: keys }) })
    ElMessage.success('保存成功')
    permDialog.value = false
  } catch (e: any) {
    try { const body = await e.json(); permError.value = body.message || '保存失败' } catch { permError.value = '保存失败' }
    ElMessage.error(permError.value)
  }
}

function closeModal() {
  showModal.value = false
}

async function submit() {
  error.value = ''
  try {
    if (formRef.value) {
      await formRef.value.validate()
    } else {
      if (!editing.role_name) {
        error.value = '请填写角色名'
        ElMessage.error(error.value)
        return
      }
    }

    // permission guard
    if (editing.id && !canEdit.value) { ElMessage.error('没有编辑权限'); return }
    if (!editing.id && !canAdd.value) { ElMessage.error('没有新增权限'); return }

    if (editing.id) {
      const payload: any = { role_name: editing.role_name, role_code: editing.role_code, role_level: editing.role_level, role_desc: editing.role_desc }
      await auth.apiFetch(`/roles/${editing.id}`, { method: 'PUT', body: JSON.stringify(payload) })
      ElMessage.success('编辑成功')
    } else {
      const payload: any = { role_name: editing.role_name, role_code: editing.role_code, role_level: editing.role_level, role_desc: editing.role_desc }
      await auth.apiFetch('/roles', { method: 'POST', body: JSON.stringify(payload) })
      ElMessage.success('新增成功')
    }
    closeModal()
    await load()
  } catch (e: any) {
    try { const body = await e.json(); error.value = body.message || '操作失败' } catch { error.value = '操作失败' }
    ElMessage.error(error.value)
  }
}

async function remove(r: any) {
  error.value = ''
  if (!canDelete.value) { ElMessage.warning('没有删除权限'); return }
  ElMessageBox.confirm(`确定删除角色 ${r.role_name || r.name} ?`, '删除确认', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(async () => {
    try {
      await auth.apiFetch(`/roles/${r.id}`, { method: 'DELETE' })
      ElMessage.success('删除成功')
      if (roles.value.length === 1 && page.value > 1) page.value = page.value - 1
      await load()
    } catch (e: any) {
      try { const body = await e.json(); error.value = body.message || '删除失败' } catch { error.value = '删除失败' }
      ElMessage.error(error.value)
    }
  }).catch(() => {})
}

function gotoPage(n: number) {
  if (n < 1) n = 1
  if (n > totalPages.value) n = totalPages.value
  page.value = n
  load()
}

function onSizeChange(size: number) {
  pageSize.value = size
  gotoPage(1)
}

function onSortChange(sort: any) {
  const column = sort && sort.column ? sort.column : null
  const prop = sort && sort.prop ? sort.prop : (column ? (column.property ?? column.prop ?? null) : null)
  const orderRaw = sort && sort.order ? sort.order : null

  let order: string | null = null
  if (orderRaw) {
    order = (orderRaw === 'ascending' || orderRaw === 'asc') ? 'asc' : 'desc'
  } else if (prop && prop === sortBy.value) {
    if (sortOrder.value === 'asc') order = 'desc'
    else if (sortOrder.value === 'desc') { sortBy.value = null; sortOrder.value = null }
    else order = 'asc'
  }

  if (!prop || !order) {
    sortBy.value = null
    sortOrder.value = null
  } else {
    sortBy.value = String(prop)
    sortOrder.value = order as 'asc' | 'desc'
  }

  page.value = 1
  load()
}

function onHeaderClick(column: any) {
  if (!column) return
  const prop = column.property ?? column.prop ?? null
  if (!prop) return
  if (!column.sortable && column.sortable !== 'custom' && column.sortable !== true) return

  if (sortBy.value !== prop) {
    sortBy.value = String(prop)
    sortOrder.value = 'asc'
  } else {
    if (sortOrder.value === 'asc') sortOrder.value = 'desc'
    else if (sortOrder.value === 'desc') { sortBy.value = null; sortOrder.value = null }
    else sortOrder.value = 'asc'
  }
  page.value = 1
  load()
}

function clearSearch() {
  searchQuery.value = ''
  page.value = 1
  load()
}

watch(searchQuery, () => {
  if (searchTimer.value) clearTimeout(searchTimer.value)
  searchTimer.value = window.setTimeout(() => {
    page.value = 1
    load()
  }, 350)
})
</script>

<style scoped>
/* simple styles consistent with project */
</style>
